﻿using System;
using NHibernate;
using NHibernate.Cfg;
using System.Runtime.Remoting.Messaging;

namespace KKCai.TC.App.Core
{
    internal sealed class SessionManager
    {
        #region Thread-safe, lazy Singleton

        public static SessionManager Instance
        {
            get
            {
                return Nested.SessionManager;
            }
        }

        private SessionManager()
        {
            InitSessionFactory();
        }

        private class Nested
        {
            static Nested() { }
            internal static readonly SessionManager SessionManager = new SessionManager();
        }

        #endregion

        private void InitSessionFactory()
        {
            sessionFactory = new Configuration().Configure().BuildSessionFactory();
        }

        public ISession GetSession()
        {
            ISession session = ContextSession;

            if (session == null)
            {
                session = sessionFactory.OpenSession();

                ContextSession = session;
            }

            return session;
        }

        public void CloseSession()
        {
            ISession session = ContextSession;

            if (session != null && session.IsOpen)
            {
                session.Flush();
                session.Close();
            }

            ContextSession = null;
        }

        public void BeginTransaction()
        {
            ITransaction transaction = ContextTransaction;

            if (transaction == null)
            {
                transaction = GetSession().BeginTransaction();
                ContextTransaction = transaction;
            }
        }

        public void CommitTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (HasOpenTransaction())
                {
                    transaction.Commit();
                    ContextTransaction = null;
                }
            }
            catch (HibernateException)
            {
                RollbackTransaction();
                throw;
            }
        }

        public bool HasOpenTransaction()
        {
            ITransaction transaction = ContextTransaction;

            return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack;
        }

        public void RollbackTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (HasOpenTransaction())
                {
                    transaction.Rollback();
                }

                ContextTransaction = null;
            }
            finally
            {
                CloseSession();
            }
        }

        private ITransaction ContextTransaction
        {
            get
            {
                return CallContext.GetData(TRANSACTION_KEY) as ITransaction;
            }
            set
            {
                CallContext.SetData(TRANSACTION_KEY, value);
            }
        }

        private ISession ContextSession
        {
            get
            {
                return CallContext.GetData(SESSION_KEY) as ISession;
            }
            set
            {
                CallContext.SetData(SESSION_KEY, value);
            }
        }

        private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION_SC_TC";
        private const string SESSION_KEY = "CONTEXT_SESSION_SC_TC";
        private ISessionFactory sessionFactory;
    }
}
